// Package web
// Created by GoLand
// @User: lenora
// @Date: 2024/1/3
// @Time: 12:35

package web

import (
	"camp/microbook/internal/service"
	"camp/microbook/internal/service/oauth2/wechat"
	ijwt "camp/microbook/internal/web/jwt"
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"github.com/google/uuid"
	"github.com/lithammer/shortuuid/v4"
)

type OAuth2WechatHandler struct {
	svc       wechat.Service
	userSrv   service.UserService
	jwt       ijwt.Handler
	stateName string
}

func NewWechatHandler(svc wechat.Service, userSrv service.UserService) *OAuth2WechatHandler {
	return &OAuth2WechatHandler{
		svc:       svc,
		userSrv:   userSrv,
		stateName: "x-jwt-state",
	}
}

func (h *OAuth2WechatHandler) RegisterRoutes(engine *gin.Engine) {
	g := engine.Group("/oauth2/wechat")
	g.GET("/auth_url")
	g.Any("/callback", h.CallBack)
}

func (h *OAuth2WechatHandler) OAuthUrl(ctx *gin.Context) {
	state := shortuuid.New() //短id
	//state := uuid.New() //长id
	url := h.svc.AuthURL(state)

	err := h.setState(ctx, state)
	if err != nil {
		webResp.InternalServiceError(ctx, err.Error())
		return
	}
	webResp.ResponseSuccess(ctx, url)
	return
}

func (h *OAuth2WechatHandler) CallBack(ctx *gin.Context) {
	if err := h.verifyState(ctx); err != nil {
		webResp.InternalServiceError(ctx, err.Error())
		return
	}
	code := ctx.Query("code")
	info, err := h.svc.VerifyCode(ctx, code)
	if err != nil {
		webResp.InternalServiceError(ctx, err.Error())
		return
	}

	u, err := h.userSrv.WechatFindOrCreate(ctx, info.Openid)
	if err != nil {
		webResp.InternalServiceError(ctx, err.Error())
		return
	}

	ssid := uuid.New().String()
	if err := h.jwt.SetJWTToken(ctx, u.Id, ssid); err != nil {
		webResp.InternalServiceError(ctx)
		return
	}
	webResp.ResponseSuccess(ctx, nil)
	return

}

type StateClaims struct {
	State string `json:"state"`
	jwt.RegisteredClaims
}

func (h *OAuth2WechatHandler) setState(ctx *gin.Context, state string) error {
	claims := StateClaims{
		State:            state,
		RegisteredClaims: jwt.RegisteredClaims{},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	tokenStr, err := token.SignedString(ijwt.JWTKey)
	if err != nil {
		return err
	}
	ctx.SetCookie(h.stateName, tokenStr,
		600,
		"oauth2/wechat/callback",
		"",
		false, //是否启用https
		true,  //是否只能使用http请求
	)
	return nil
}

func (h *OAuth2WechatHandler) verifyState(ctx *gin.Context) error {
	state := ctx.Query("states")
	ckie, err := ctx.Cookie(h.stateName)
	if err != nil {
		return err
	}
	var claims StateClaims
	_, err = jwt.ParseWithClaims(ckie, &claims, func(token *jwt.Token) (interface{}, error) {
		return ijwt.JWTKey, nil
	})
	if err != nil {
		return err
	}
	if state != claims.State {
		return errors.New("state not match !")
	}
	return nil
}
